<template>
  <div class="page_wp">
    <div class="header_wp">
      <div class="logo_wp">
        <img src="./assets/images/logo.png" />
      </div>
      <div class="user_wp">
        <img src="./assets/images/user_icon.png" />
      </div>
    </div>
    <div class="chat_wp">
      <div class="chat_new_wp" v-show="!conversationMessageList.length">
        <div class="new_wp">
          <div class="new_title">
            <div class="title_logo">
              <img src="./assets/images/logo.png" />
            </div>
          </div>
          <div class="new_text">安踏AIGS研发专家</div>
        </div>
      </div>
      <div class="chat_list_wp" ref="chatListRef" v-show="conversationMessageList.length">
        <template v-for="item in conversationMessageList">
          <div class="assistant_chat_item chat_item" v-if="item.role == 'assistant'">
            <div class="chat_cell_info">
              <div class="chat_cell_avater">
                <img src="./assets/images/logo_icon.png" />
              </div>
              <div class="chat_cell_name">chatGLM</div>
            </div>
            <div class="chat_content" v-if="!item.loading" v-html="item.content.replace(/{([^}]*)}|{([^}]*)$/g, '')">
            </div>
            <div class="chat_content" v-if="!item.loading"
              v-for="items in item.content.match(/{([^}]+)}/g)?.map((match: any) => match.slice(1, -1))">
              <video class="chat_video" autoplay :ref="setVideoRef" :src="items" controls width="600">
                您的浏览器不支持视频播放
              </video>
            </div>
            <div class="loading-dots" v-else>
              <span class="dot"></span>
              <span class="dot"></span>
              <span class="dot"></span>
            </div>
          </div>
          <div class="user_chat_item chat_item" v-if="item.role == 'user'">
            <div class="chat_cell_info">
              <div class="chat_cell_name">chatGLM</div>
              <div class="chat_cell_avater">
                <img src="./assets/images/user_icon.png" />
              </div>
            </div>
            <div class="chat_content" v-html="item.content">
            </div>
          </div>
        </template>
      </div>
      <div class="chat_text_wp">
        <div v-show="isSendText" class="chat_input_wp">
          <textarea v-model="sendMessage" @keydown.enter.exact.prevent="handleSendMessage"
            @keyup.enter.exact.prevent="sendMessage = undefined" class="chat_input" placeholder="给我发消息吧" />
          <div class="chat_btns">
            <div class="btn" @click="handleSwitchSendType">
              <img src="./assets/images/audio_icon.png" />
            </div>
            <div class="btn" @click="handleSendMessage">
              <img src="./assets/images/send_icon.png" />
            </div>
          </div>
        </div>

        <div v-show="!isSendText" class="chat_audio_wp">
          <div class="audio_text_wp">
            <div class="text" v-show="isRecording">松手发送</div>
          </div>
          <div class="audio_btn_wp">
            <div class="text">按住空格说话</div>
            <div class="chat_btns">
              <div class="btn" @click="handleSwitchSendType">
                <img src="./assets/images/keyboard_icon.png" />
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
import { ref, nextTick, onMounted, onUnmounted } from "vue"
import { create_conversation, create_conversation_message, get_conversation_message_list, send_chat, retrieve_chat } from "./api"
import mp3 from "@/assets/audio/637b14c7f12c0392.mp3"

const BASEURL = import.meta.env.VITE_API_BASE_URL;
const TOKEN = import.meta.env.VITE_API_TOKEN;
const videoPlayers = ref<any>([])
const isSendText = ref(true)
const isRecording = ref(false)
let chatListRef = ref()

// 设置视频引用
const setVideoRef = (el: any) => {
  if (el) {
    videoPlayers.value.push(el)
  }
}

let sendMessageLoading = ref(false)
let sendMessage = ref()
let botId = import.meta.env.VITE_API_BOT_ID;
let conversationInfo = ref<any>(JSON.parse(sessionStorage.getItem("_CONVERSATION_INFO") || "{}"));
let conversationMessageList = ref<any>(JSON.parse(sessionStorage.getItem("_CONVERSATION_LIST") || "{}"));

async function sendAudioToText(base64File: any) {
  const url = 'https://openspeech.bytedance.com/api/v3/auc/bigmodel/recognize/flash'
  const pramas =
  {
    "user": {
      "uid": "8623896844"
    },
    "audio": {
      "data": base64File
    },
    "request": {
      "model_name": "bigmodel"
    }
  }

  const response = await fetch(url, {
    method: 'POST', body: JSON.stringify(pramas),
    headers: {
      'X-Api-App-Key': '8623896844',
      'X-Api-Access-Key': 'qfdAJ8zIyADZNExcBCdCeBaxRJS9HJyU',
      'X-Api-Resource-Id': 'volc.bigasr.auc_turbo',
      'X-Api-Request-Id': `${+new Date()}`,
      'X-Api-Sequence': '-1'
    }
  });
  return response
}

function parseStreamAndAggregate(sseString: any) {
  let finalObject: any = null;
  let fullContent = "";

  const eventBlocks = sseString.trim().split('\n\n');

  eventBlocks.forEach((block: any) => {
    if (block.includes('event:conversation.message.delta')) {
      const lines = block.split('\n');
      for (const line of lines) {
        if (line.startsWith('data:')) {
          const jsonString = line.substring(5).trim();
          try {
            const dataObject = JSON.parse(jsonString);

            if (dataObject.content) {
              fullContent += dataObject.content;
            }

            finalObject = dataObject;

          } catch (e) {
            console.error("解析 JSON 失败:", jsonString, e);
          }
        }
      }
    }
  });
  
  if (finalObject) {
    finalObject.content = fullContent;
  }

  return finalObject;
}

async function sendChatStreamData(pramas: any) {
  const url = `${BASEURL}/v3/chat?conversation_id=${pramas.conversation_id}`
  const response = await fetch(url, { method: 'POST', body: JSON.stringify(pramas), headers: { Accept: "application/json", 'Content-Type': 'application/json', "Authorization": `Bearer ${TOKEN}` } });
  const reader = response.body!.getReader();
  const textDecoder = new TextDecoder();
  let newAssistantMessage = {
    "bot_id": botId,
    "chat_id": "",
    "content": "",
    "content_type": "",
    "conversation_id": "",
    "created_at": "",
    "id": "",
    "meta_data": {},
    "reasoning_content": "",
    "role": "assistant",
    "section_id": "",
    "type": "answer",
    "loading": true,
    "updated_at": "",
  }
  conversationMessageList.value.push(newAssistantMessage)
  sessionStorage.setItem("_CONVERSATION_LIST", JSON.stringify(conversationMessageList.value))
  scrollToBottom()


  let done = false;
  while (!done) {
    const { done, value } = await reader.read();

    if (done) {
      sendMessageLoading.value = false;
      console.log("传输完成")
      break;
    }

    if (value) {
      let res = parseStreamAndAggregate(textDecoder.decode(value, { stream: true })) || {}
      if (res.content) {
        conversationMessageList.value[conversationMessageList.value.length - 1] = {
          "bot_id": res.bot_id,
          "chat_id": res.chat_id,
          "content": conversationMessageList.value[conversationMessageList.value.length - 1].content + res.content,
          "content_type": res.content_type,
          "conversation_id": res.conversation_id,
          "created_at": "",
          "id": res.id,
          "meta_data": {},
          "reasoning_content": "",
          "role": res.role,
          "section_id": res.section_id,
          "type": "answer",
          "loading": false,
          "updated_at": "",
        }
        window.sessionStorage.setItem("_CONVERSATION_LIST", JSON.stringify(conversationMessageList.value))
        scrollToBottom()
      }
    }
  }
}

const scrollToBottom = () => {
  nextTick(() => {
    if (chatListRef.value) {
      chatListRef.value.scrollTop = chatListRef.value.scrollHeight;
    }
  });
};

const createConversation = () => {
  const pramas = {
    bot_id: botId
  }
  create_conversation(pramas, (res: any) => {
    if (res.code == 0) {
      conversationInfo.value = res.data
      sessionStorage.setItem("_CONVERSATION_INFO", JSON.stringify(res.data))
      getConversationMessageList()
    }
  })
}

const getConversationMessageList = () => {
  const listPramas = {
    conversation_id: conversationInfo.value.id
  }
  get_conversation_message_list(listPramas, (res: any) => {
    if (res.code == 0) {
      conversationMessageList.value = res.data.toReversed()
      sessionStorage.setItem("_CONVERSATION_LIST", JSON.stringify(conversationMessageList.value))
      scrollToBottom()
    }
  })
}

if (Object.keys(conversationInfo.value).length == 0) {
  createConversation()
} else {
  getConversationMessageList()
}

const handleSendMessage = async () => {
  videoPlayers.value.forEach((video: any) => {
    video.pause()
  })
  if (sendMessage.value?.trim() === "" || sendMessage.value === undefined || sendMessageLoading.value) {
    return
  }
  const mssagePramas = {
    conversation_id: conversationInfo.value.id,
    role: "user",
    content: sendMessage.value,
    content_type: "text"
  }
  sendMessageLoading.value = true;
  sendMessage.value = undefined
  await create_conversation_message(mssagePramas, async (res: any) => {
    if (res.code == 0) {
      let newUserMessage = {
        "bot_id": botId,
        "chat_id": -new Date(),
        "content": res.data.content,
        "content_type": res.data.content_type,
        "conversation_id": res.data.conversation_id,
        "created_at": res.data.created_at,
        "id": -new Date(),
        "meta_data": {},
        "reasoning_content": "",
        "role": "user",
        "section_id": res.data.conversation_id,
        "type": "answer",
        "updated_at": res.data.created_at
      }
      conversationMessageList.value.push(newUserMessage)
      sessionStorage.setItem("_CONVERSATION_LIST", JSON.stringify(conversationMessageList.value))
      scrollToBottom()

      const sendPramas = {
        conversation_id: conversationInfo.value.id,
        bot_id: botId,
        user_id: "9999",
        stream: true
      }
      await sendChatStreamData(sendPramas)
    }
  })
}


const getBase64 = async (mp3: any) => {
  const base64 = await fetch(mp3)
    .then(r => r.blob())
    .then(blob => new Promise(resolve => {
      const reader = new FileReader()
      reader.onload = () => resolve((reader.result as string).split(',')[1])
      reader.readAsDataURL(blob)
    }))
  return base64
}


// 全局监听版本
const handleGlobalKeyDown = (event: any): void => {
  if (event.code === 'Space' && !isRecording.value && !isSendText.value) {
    event.preventDefault()
    isRecording.value = true
  }
}

const handleGlobalKeyUp = (event: any): void => {
  if (event.code === 'Space') {
    isRecording.value = false
  }
}

onMounted((): void => {
  document.addEventListener('keydown', handleGlobalKeyDown)
  document.addEventListener('keyup', handleGlobalKeyUp)
})

onUnmounted((): void => {
  document.removeEventListener('keydown', handleGlobalKeyDown)
  document.removeEventListener('keyup', handleGlobalKeyUp)
})
const handleSwitchSendType = () => {
  isSendText.value = !isSendText.value
}

</script>

<style lang="scss">
@use "./assets/css/index.scss" as *;
</style>
<style lang="scss" scoped>
.page_wp {
  height: 100%;
}

.header_wp {
  padding: 0 100px;
  height: 108px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0;

  .logo_wp {
    font-size: 0;

    img {
      width: 120px;
      height: 64px;
    }
  }

  .user_wp {
    img {
      width: 48px;
      height: 48px;
    }
  }
}

.chat_wp {
  height: calc(100% - 200px);
  margin: 50px auto;
  width: 910px;
  display: flex;
  flex-direction: column;

  .chat_new_wp {
    position: relative;
    flex: 1;

    .new_wp {
      width: 100%;
      position: absolute;
      left: 50%;
      top: 40%;
      transform: translate(-50%, -50%);

      .new_title {
        display: flex;
        align-items: center;
        font-weight: 600;
        font-size: 32px;
        line-height: 100%;
        letter-spacing: 0%;
        justify-content: center;

        .title_logo {
          font-size: 0;

          img {
            width: 150px;
            height: 83px;
          }
        }
      }

      .new_text {
        padding-top: 20px;
        font-size: 32px;
        line-height: 100%;
        text-align: center;
        color: #666666;
      }

      .new_hghlights {
        padding-top: 60px;
        text-align: center;

        .highlight {
          margin-right: 20px;
          display: inline-block;
          padding: 10px 20px;
          border-radius: 14px;
          font-weight: 500;
          font-size: 16px;
          line-height: 22px;
          background-color: #F9F9F9;

          .highlight_title {
            margin-right: 10px;
            color: #D51A21;
          }

          &:first-child {
            background: linear-gradient(285.06deg, #F9F9F9 -5.51%, #F8F8F8 54.27%, #F2BABC 92.6%);
          }

          &:last-child {
            background: linear-gradient(285.06deg, #F9F9F9 -5.51%, #F8F8F8 54.27%, #F2BABC 92.6%);
            margin-right: 0px;
          }
        }
      }
    }
  }

  .chat_list_wp {
    padding: 0 20px;
    width: 100%;
    position: relative;
    flex: 1;
    overflow-y: auto;

    .chat_item {
      padding: 20px 0;
      border-top: 1px dashed #D9D9D9;

      .chat_cell_info {
        display: flex;
        align-items: center;

        .chat_cell_avater {
          margin-right: 22px;
          width: 25px;
          height: 25px;
          border-radius: 50%;
          overflow: hidden;
          font-size: 0;

          img {
            width: 100%;
            height: 100%;
          }
        }

        .chat_cell_name {
          font-size: 18px;
          font-weight: 600;

        }
      }

      .chat_content {
        padding-bottom: 10px;

        .chat_video {
          width: 650px;
          height: 330px;
          border-radius: 20px;
        }
      }

      &:first-child {
        border: 0;
      }
    }

    .assistant_chat_item {
      text-align: left;

      .chat_content {
        padding-top: 10px;
        padding-left: 47px;
      }
    }

    .user_chat_item {
      text-align: right;

      .chat_cell_info {
        justify-content: right;

        .chat_cell_avater {
          margin: 0;
          margin-left: 22px;
        }
      }

      .chat_content {
        padding-top: 10px;
        padding-right: 47px;
      }
    }
  }

  .chat_text_wp {
    padding-top: 60px;

    .chat_input_wp,
    .chat_audio_wp {
      display: flex;
      flex-direction: column;
      padding: 15px 20px;
      width: 100%;
      border: 1px solid #E5E7E6;
      background-color: #F9FBFF;
      border-radius: 20px;

      .chat_input {
        resize: none;
        padding: 0;
        margin: 0;
        width: 100%;
        height: 100px;
        box-sizing: border-box;
        border: 0;
        background-color: #F9FBFF;
        font-size: 18px;
        outline: none;
      }

      .chat_btns {
        flex: 1;
        display: flex;
        font-size: 0;
        justify-content: right;

        .btn {
          cursor: pointer;
          display: flex;
          font-size: 0;
          margin-right: 15px;
          align-items: center;

          img {
            width: 34px;
            height: 34px;
          }

          &:last-child {
            margin-right: 0;
          }
        }
      }
    }

    .chat_audio_wp {
      padding: 0;
      border: 0;
      background-color: #fff;

      .audio_text_wp {
        position: relative;
        text-align: center;
        height: 68px;

        .text {
          position: absolute;
          left: 50%;
          transform: translateX(-50%);
          bottom: 20px;
          width: 228px;
          height: 52px;
          line-height: 52px;
          border-radius: 14px;
          border: 1px solid #E5E7E6;
          background-color: #F9FBFF;
          color: #999999;
          font-size: 16px;
          text-align: center;

        }
      }

      .audio_btn_wp {
        height: 98px;
        padding: 0 20px;
        display: flex;
        border-radius: 20px;
        border: 1px solid #E5E7E6;
        background-color: #F9FBFF;

        .text {
          cursor: pointer;
          flex: 1;
          text-align: center;
          line-height: 98px;
          color: #777777;
          font-weight: 600;
          font-size: 18px;
          -webkit-user-select: none;
          -moz-user-select: none;
          -ms-user-select: none;
          user-select: none;
        }

        .chat_btns {
          width: 0;
          flex: none;
        }
      }
    }
  }
}

/*
 * 容器样式
 * 使用 Flexbox 让三个点水平居中排列
 */
.loading-dots {
  padding-bottom: 10px;
  padding-top: 20px;
  padding-left: 47px;
  display: flex;
  justify-content: left;
  align-items: center;
  gap: 10px;
  /* 三个点之间的间距 */
}

/*
 * 单个点的样式
 */
.dot {
  width: 12px;
  height: 12px;
  background-color: #999999;
  /* 点的颜色 */
  border-radius: 50%;
  /* 变成圆形 */

  /* 应用动画 */
  animation: bounce 1.4s infinite ease-in-out both;
}

/*
 * 使用 :nth-child 为每个点设置动画延迟
 * 这是创建“波浪”效果的关键
 */
.dot:nth-child(1) {
  animation-delay: -0.32s;
}

.dot:nth-child(2) {
  animation-delay: -0.16s;
}

.dot:nth-child(3) {
  animation-delay: 0s;
}

/*
 * 定义动画关键帧
 * 动画效果是让点在Y轴上移动并缩放
 */
@keyframes bounce {

  0%,
  80%,
  100% {
    transform: scale(0);
  }

  40% {
    transform: scale(1.0);
  }
}
</style>
